package com.rfid;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;

import com.dense.kuiniu.bookborrow.BuildConfig;
import com.hoho.android.usbserial.driver.CdcAcmSerialDriver;
import com.hoho.android.usbserial.driver.ProbeTable;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.hoho.android.usbserial.util.SerialInputOutputManager;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.Executors;

import androidx.annotation.NonNull;

/**
 * @author Administrator
 * @date 2020/11/28
 */
public class UsbReader {
    private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";
    private Context mContext;
    private int vendorId;
    private int productId;
    private BroadcastReceiver broadcastReceiver;
    private SerialInputOutputManager.Listener listener;
    private UsbPermission usbPermission = UsbPermission.Unknown;
    private UsbSerialPort usbSerialPort;
    private SerialInputOutputManager usbIoManager;
    private boolean connected = false;

    private enum UsbPermission {Unknown, Requested, Granted, Denied}

    public UsbReader(Context context, int vendorId, int productId,
                     @NonNull SerialInputOutputManager.Listener listener) {
        mContext = context;
        this.vendorId = vendorId;
        this.productId = productId;
        this.listener = listener;
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
                    usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
                            ? UsbPermission.Granted : UsbPermission.Denied;
                    start();
                }
            }
        };
        if (mContext != null) {
            mContext.registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));
        }
    }


    public void stop() {
        if (mContext != null && broadcastReceiver != null) {
            mContext.unregisterReceiver(broadcastReceiver);
        }
        disconnect();
    }


    public void start() {
        if (mContext != null) {
            UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
            if (usbManager == null) {
                listener.onRunError(new Exception("服务获取失败"));
                return;
            }
            HashMap<String, UsbDevice> map = usbManager.getDeviceList();
            UsbDevice usbDevice = null;
            if (map == null) {
                listener.onRunError(new Exception("设备列表获取失败"));
                return;
            }
            for (UsbDevice device : map.values()) {
                int vendorId = device.getVendorId();
                int productId = device.getProductId();
                if (this.vendorId == vendorId && this.productId == productId) {
                    usbDevice = device;
                    break;
                }
            }
            if (usbDevice == null) {
                listener.onRunError(new Exception("设备未连接"));
                return;
            }
            UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(usbDevice);
            if (driver == null) {
                //查找自定义
                ProbeTable customTable = new ProbeTable();
                customTable.addProduct(usbDevice.getVendorId(),
                        usbDevice.getProductId(), CdcAcmSerialDriver.class);
                driver = new UsbSerialProber(customTable).probeDevice(usbDevice);
            }
            if (driver == null) {
                listener.onRunError(new Exception("未找到设备驱动"));
                return;
            }
            usbSerialPort = driver.getPorts().get(0);
            if (usbSerialPort == null) {
                listener.onRunError(new Exception("设备端口获取失败"));
                return;
            }
            UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice());
            if (usbConnection == null && usbPermission == UsbPermission.Unknown
                    && !usbManager.hasPermission(driver.getDevice())) {
                usbPermission = UsbPermission.Requested;
                PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(mContext,
                        0, new Intent(INTENT_ACTION_GRANT_USB), 0);
                usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
                return;
            }
            if (usbConnection == null) {
                if (!usbManager.hasPermission(driver.getDevice())) {
                    listener.onRunError(new Exception("未申请权限"));
                } else {
                    listener.onRunError(new Exception("连接打开失败"));
                }
                return;
            }
            try {
                usbSerialPort.open(usbConnection);
                usbSerialPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
                usbIoManager = new SerialInputOutputManager(usbSerialPort, this.listener);
                Executors.newSingleThreadExecutor().submit(usbIoManager);
                connected = true;
            } catch (Exception e) {
                listener.onRunError(new Exception("端口打开异常:" + e.getMessage()));
                disconnect();
            }
        }
    }

    private void disconnect() {
        connected = false;
        if (usbIoManager != null)
            usbIoManager.stop();
        usbIoManager = null;
        try {
            if (usbSerialPort != null) {
                usbSerialPort.close();
            }
        } catch (IOException ignored) {
        }
        usbSerialPort = null;
    }

    public void send(byte[] data) {
        if (usbSerialPort != null && connected) {
            try {
                usbSerialPort.write(data, 10);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}